package com.learn.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.learn.exception.JacksonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * jackson工具类
 *
 * @author yaoqihui
 * @version 2022/3/15 0015
 * @see [相关类/方法]
 */
@Slf4j
public class JsonUtil
{
	private JsonUtil ()
	{

	}

	private static ObjectMapper mapper;

	/**
	 * 序列化级别，默认只序列化不为空的字段
	 */
	protected static final JsonInclude.Include DEFAULT_PROPERTY_INCLUSION = JsonInclude.Include.NON_NULL;

	/**
	 * 是否缩进JSON格式
	 */
	protected static final boolean IS_ENABLE_INDENT_OUTPUT = false;

	static
	{
		try
		{
			//初始化
			mapper = new ObjectMapper ();
			//配置序列化级别
			mapper.setSerializationInclusion (DEFAULT_PROPERTY_INCLUSION);
			//配置JSON缩进支持
			mapper.configure (SerializationFeature.INDENT_OUTPUT, IS_ENABLE_INDENT_OUTPUT);
			//配置普通属性
			config (mapper);
		}
		catch (Exception e)
		{
			log.error ("jackson config error", e);
		}
	}

	protected static void config (ObjectMapper objectMapper)
	{
		//序列化BigDecimal时之间输出原始数字还是科学计数, 默认false, 即是否以toPlainString()科学计数方式来输出
		objectMapper.enable (JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
		//允许将JSON空字符串强制转换为null对象值
		objectMapper.enable (DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

		//允许单个数值当做数组处理
		objectMapper.enable (DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

		//禁止重复键, 抛出异常
		objectMapper.enable (DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
		//禁止使用int代表Enum的order()來反序列化Enum, 抛出异常
		objectMapper.enable (DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
		//有属性不能映射的时候不报错
		objectMapper.disable (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		//使用null表示集合类型字段是时不抛异常
		objectMapper.disable (DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
		//对象为空时不抛异常
		objectMapper.disable (SerializationFeature.FAIL_ON_EMPTY_BEANS);

		//允许在JSON中使用c/c++风格注释
		objectMapper.enable (JsonParser.Feature.ALLOW_COMMENTS);
		//允许未知字段
		objectMapper.enable (JsonGenerator.Feature.IGNORE_UNKNOWN);
		//在JSON中允许未引用的字段名
		objectMapper.enable (JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
		//时间格式
		objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
		objectMapper.setDateFormat (new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH));
		//识别单引号
		objectMapper.enable (JsonParser.Feature.ALLOW_SINGLE_QUOTES);
		//识别Java8时间
		objectMapper.registerModule (new ParameterNamesModule ());
		objectMapper.registerModule (new Jdk8Module ());
		objectMapper.registerModule (new JavaTimeModule ());
	}

	public static ObjectMapper getObjectMapper ()
	{
		return mapper;
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (URL url, Class<V> type)
	{
		try
		{
			return mapper.readValue (url, type);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, url: {}, type:  {}", url.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (URL url, TypeReference<V> type)
	{
		try
		{
			return mapper.readValue (url, type);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, url: {}, type: {}", url.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化（List）
	 */
	public static <V> List<V> fromList (URL url, Class<V> type)
	{
		try
		{
			CollectionType collectionType = mapper.getTypeFactory ().constructCollectionType (ArrayList.class, type);
			return mapper.readValue (url, collectionType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, url: {}, type: {}", url.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (InputStream inputStream, Class<V> type)
	{
		try
		{
			return mapper.readValue (inputStream, type);
		}
		catch (IOException e)
		{
			log.error ("jackson  from  error , type: {}", type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (InputStream inputStream, TypeReference<V> type)
	{
		try
		{
			return mapper.readValue (inputStream, type);
		}
		catch (IOException e)
		{
			log.error ("jackson  from  error , type: {}", type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化（List）
	 */
	public static <V> List<V> fromList (InputStream inputStream, Class<V> type)
	{
		try
		{
			CollectionType collectionType = mapper.getTypeFactory ().constructCollectionType (ArrayList.class, type);
			return mapper.readValue (inputStream, collectionType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, type: {}", type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (File file, Class<V> type)
	{
		try
		{
			return mapper.readValue (file, type);
		}
		catch (IOException e)
		{
			log.error ("jackson  from error ,  file path: {}, type: {}", file.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (File file, TypeReference<V> type)
	{
		try
		{
			return mapper.readValue (file, type);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, file  path: {}, type: {}", file.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化（List）
	 */
	public static <V> List<V> fromList (File file, Class<V> type)
	{
		try
		{
			CollectionType collectionType = mapper.getTypeFactory ().constructCollectionType (ArrayList.class, type);
			return mapper.readValue (file, collectionType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error , file path: {} , type: {}", file.getPath (), type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (String json, Class<V> type)
	{
		return from (json, (Type) type);
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (String json, TypeReference<V> type)
	{
		return from (json, type.getType ());
	}

	/**
	 * JSON反序列化
	 */
	public static <V> V from (String json, Type type)
	{
		if (json == null)
		{
			return null;
		}
		try
		{
			JavaType javaType = mapper.getTypeFactory ().constructType (type);
			return mapper.readValue (json, javaType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, json : {}, type : {}", json, type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化（List）
	 */
	public static <V> List<V> fromList (String json, Class<V> type)
	{
		if (json == null)
		{
			return Collections.emptyList ();
		}
		try
		{
			CollectionType collectionType = mapper.getTypeFactory ().constructCollectionType (ArrayList.class, type);
			return mapper.readValue (json, collectionType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, json: {}, type: {}", json, type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * JSON反序列化（Map）
	 */
	public static Map<String, Object> fromMap (String json)
	{
		if (json == null)
		{
			return Collections.emptyMap ();
		}
		try
		{
			MapType mapType = mapper.getTypeFactory ().constructMapType (HashMap.class, String.class, Object.class);
			return mapper.readValue (json, mapType);
		}
		catch (IOException e)
		{
			log.error ("jackson from error, json: {}", json, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 序列化为JSON
	 */
	public static <V> String to (List<V> list)
	{
		try
		{
			return mapper.writeValueAsString (list);
		}
		catch (JsonProcessingException e)
		{
			log.error (" jackson to error, data: {}", list, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 序列化为JSON
	 */
	public static <V> String to (V v)
	{
		try
		{
			return mapper.writeValueAsString (v);
		}
		catch (JsonProcessingException e)
		{
			log.error (" jackson to error, data: {}", v, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 序列化为JSON
	 */
	public static <V> void toFile (String path, List<V> list)
	{
		try (Writer writer = new FileWriter (path, true))
		{
			mapper.writer ().writeValues (writer).writeAll (list);
		}
		catch (Exception e)
		{
			log.error ("jackson to file error, path: {}, list: {}", path, list, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 序列化为JSON
	 */
	public static <V> void toFile (String path, V v)
	{
		try (Writer writer = new FileWriter (path, true))
		{
			mapper.writer ().writeValues (writer).write (v);
		}
		catch (Exception e)
		{
			log.error ("jackson to file error, path: {}, data: {}", path, v, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return String，默认为 null
	 */
	public static String getAsString (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return null;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return null;
			}
			return getAsString (jsonNode);
		}
		catch (Exception e)
		{
			log.error ("jackson get string error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	private static String getAsString (JsonNode jsonNode)
	{
		return jsonNode.isTextual () ? jsonNode.textValue () : jsonNode.toString ();
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return int，默认为 0
	 */
	public static int getAsInt (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return 0;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return 0;
			}
			return jsonNode.isInt () ? jsonNode.intValue () : Integer.parseInt (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get int error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return Integer，默认为 null
	 */
	public static Integer getAsInteger (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return null;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return null;
			}
			return jsonNode.isInt () ? jsonNode.intValue () : Integer.parseInt (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get Integer error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return long，默认为 0
	 */
	public static long getAsLong (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return 0L;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return 0L;
			}
			return jsonNode.isLong () ? jsonNode.longValue () : Long.parseLong (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get long error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return double，默认为 0.0
	 */
	public static double getAsDouble (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return 0.0;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return 0.0;
			}
			return jsonNode.isDouble () ? jsonNode.doubleValue () : Double.parseDouble (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get double error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return BigInteger，默认为 0.0
	 */
	public static BigInteger getAsBigInteger (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return new BigInteger (String.valueOf (0.00));
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return new BigInteger (String.valueOf (0.00));
			}
			return jsonNode.isBigInteger () ? jsonNode.bigIntegerValue () : new BigInteger (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get big integer error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return BigDecimal，默认为 0.00
	 */
	public static BigDecimal getAsBigDecimal (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return new BigDecimal ("0.00");
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return new BigDecimal ("0.00");
			}
			return jsonNode.isBigDecimal () ? jsonNode.decimalValue () : new BigDecimal (getAsString (jsonNode));
		}
		catch (Exception e)
		{
			log.error ("jackson get big decimal error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return boolean, 默认为false
	 */
	public static boolean getAsBoolean (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return false;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return false;
			}
			if (jsonNode.isBoolean ())
			{
				return jsonNode.booleanValue ();
			}
			else
			{
				if (jsonNode.isTextual ())
				{
					String textValue = jsonNode.textValue ();
					if ("1".equals (textValue))
					{
						return true;
					}
					else
					{
						return BooleanUtils.toBoolean (textValue);
					}
				}
				else
				{//number
					return BooleanUtils.toBoolean (jsonNode.intValue ());
				}
			}
		}
		catch (Exception e)
		{
			log.error ("jackson get boolean error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return byte[], 默认为 null
	 */
	public static byte[] getAsBytes (String json, String key)
	{
		if (StringUtils.isEmpty (json))
		{
			return new byte[0];
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return new byte[0];
			}
			return jsonNode.isBinary () ? jsonNode.binaryValue () : getAsString (jsonNode).getBytes ();
		}
		catch (Exception e)
		{
			log.error ("jackson get byte error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return object, 默认为 null
	 */
	public static <V> V getAsObject (String json, String key, Class<V> type)
	{
		if (StringUtils.isEmpty (json))
		{
			return null;
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return null;
			}
			JavaType javaType = mapper.getTypeFactory ().constructType (type);
			return from (getAsString (jsonNode), javaType);
		}
		catch (Exception e)
		{
			log.error ("jackson get list error, json: {}, key : {}, type: {}", json, key, type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return list, 默认为 null
	 */
	public static <V> List<V> getAsList (String json, String key, Class<V> type)
	{
		if (StringUtils.isEmpty (json))
		{
			return Collections.emptyList ();
		}
		try
		{
			JsonNode jsonNode = getAsJsonObject (json, key);
			if (null == jsonNode)
			{
				return Collections.emptyList ();
			}
			CollectionType collectionType = mapper.getTypeFactory ().constructCollectionType (ArrayList.class, type);
			return from (getAsString (jsonNode), collectionType);
		}
		catch (Exception e)
		{
			log.error ("jackson get list error, json: {}, key: {}, type: {}", json, key, type, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 从json串中获取某个字段
	 *
	 * @return JsonNode, 默认为 null
	 */
	public static JsonNode getAsJsonObject (String json, String key)
	{
		try
		{
			JsonNode node = mapper.readTree (json);
			if (null == node)
			{
				return null;
			}
			return node.get (key);
		}
		catch (IOException e)
		{
			log.error ("jackson get object from json error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 向json中添加属性
	 *
	 * @return json
	 */
	public static <V> String add (String json, String key, V value)
	{
		try
		{
			JsonNode node = mapper.readTree (json);
			add (node, key, value);
			return node.toString ();
		}
		catch (IOException e)
		{
			log.error ("jackson add error, json: {}, key: {}, value: {}", json, key, value, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 向json中添加属性
	 */
	private static <V> void add (JsonNode jsonNode, String key, V value)
	{
		if (value instanceof String)
		{
			((ObjectNode) jsonNode).put (key, (String) value);
		}
		else if (value instanceof Short)
		{
			((ObjectNode) jsonNode).put (key, (Short) value);
		}
		else if (value instanceof Integer)
		{
			((ObjectNode) jsonNode).put (key, (Integer) value);
		}
		else if (value instanceof Long)
		{
			((ObjectNode) jsonNode).put (key, (Long) value);
		}
		else if (value instanceof Float)
		{
			((ObjectNode) jsonNode).put (key, (Float) value);
		}
		else if (value instanceof Double)
		{
			((ObjectNode) jsonNode).put (key, (Double) value);
		}
		else if (value instanceof BigDecimal)
		{
			((ObjectNode) jsonNode).put (key, (BigDecimal) value);
		}
		else if (value instanceof BigInteger)
		{
			((ObjectNode) jsonNode).put (key, (BigInteger) value);
		}
		else if (value instanceof Boolean)
		{
			((ObjectNode) jsonNode).put (key, (Boolean) value);
		}
		else if (value instanceof byte[])
		{
			((ObjectNode) jsonNode).put (key, (byte[]) value);
		}
		else
		{
			((ObjectNode) jsonNode).put (key, to (value));
		}
	}

	/**
	 * 除去json中的某个属性
	 *
	 * @return json
	 */
	public static String remove (String json, String key)
	{
		try
		{
			JsonNode node = mapper.readTree (json);
			((ObjectNode) node).remove (key);
			return node.toString ();
		}
		catch (IOException e)
		{
			log.error ("jackson remove error, json: {}, key: {}", json, key, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 修改 JSON 中的属性
	 *
	 * @param json  原始 JSON 字符串
	 * @param key   需要修改的属性键
	 * @param value 修改后的值
	 * @param <V>   值的类型
	 * @return 修改后的 JSON 字符串。如果属性键不存在，则会新增该键值对；如果输入的 JSON 格式错误，则抛出异常。
	 */

	public static <V> String update (String json, String key, V value)
	{
		try
		{
			JsonNode node = mapper.readTree (json);
			((ObjectNode) node).remove (key);
			add (node, key, value);
			return node.toString ();
		}
		catch (IOException e)
		{
			log.error ("jackson update error, json: {}, key: {}, value: {}", json, key, value, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 格式化Json(美化)
	 *
	 * @param json JSON串
	 * @return 美化结果
	 */
	public static String format (String json)
	{
		try
		{
			JsonNode node = mapper.readTree (json);
			return mapper.writerWithDefaultPrettyPrinter ().writeValueAsString (node);
		}
		catch (IOException e)
		{
			log.error ("jackson format json error, json: {}", json, e);
			throw new JacksonException (e.getMessage ());
		}
	}

	/**
	 * 判断字符串是否是json
	 *
	 * @param json JSON串
	 * @return 判断结果
	 */
	public static boolean isJson (String json)
	{
		try
		{
			mapper.readTree (json);
			return true;
		}
		catch (Exception e)
		{
			return false;
		}
	}

	/**
	 * json字节数组转map
	 */
	public static Map<String, String> readValue (byte[] body)
	{
		try
		{
			return mapper.readValue (body, Map.class);
		}
		catch (JsonProcessingException e)
		{
			log.error ("json readValue exception", e);
		}
		catch (IOException e)
		{
			log.error ("readValue exception", e);
		}
		return Collections.emptyMap ();
	}
}
